﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Text;
using System.Threading;
using HttpWebAdapters;
using TC.Vacation.Spider.Loggers;

namespace TC.Vacation.Spider.Web
{
    internal class CsqWebRequest : ICsqWebRequest
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="CsqWebRequest"/> class.
        /// </summary>
        /// <param name="url">The URL.</param>
        public CsqWebRequest(string url)
        {
            Url = url;
            WebRequestFactory = Config.WebRequestFactory;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CsqWebRequest"/> class.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <param name="webRequestFactory">The web request factory.</param>
        public CsqWebRequest(string url, IHttpWebRequestFactory webRequestFactory)
        {
            Url = url;
            WebRequestFactory = webRequestFactory;
        }


        IHttpWebRequestFactory WebRequestFactory;
        Lazy<List<KeyValuePair<string, string>>> _PostData = new Lazy<List<KeyValuePair<string, string>>>();
        private ServerConfig _Options;

        /// <summary>
        /// 请求配置项
        /// </summary>
        public ServerConfig Options
        {
            get
            {
                return _Options ?? ServerConfig.Default;
            }
            set
            {
                _Options = value;
            }
        }

        /// <summary>
        /// 加载的url
        /// </summary>

        public string Url { get; protected set; }

        /// <summary>
        /// UserAgent
        /// </summary>

        public string UserAgent
        {
            get
            {
                return Options.UserAgent;
            }
            set
            {
                Options.UserAgent = value;
            }
        }

        /// <summary>
        /// 是否异步
        /// </summary>

        public bool Async { get; set; }

        /// <summary>
        /// 是否完成
        /// </summary>

        public bool Complete { get; protected set; }

        /// <summary>
        /// 超时
        /// </summary>

        public int Timeout
        {
            get
            {
                return (int)Math.Floor(Options.Timeout.TotalMilliseconds);
            }
            set
            {
                Options.Timeout = TimeSpan.FromMilliseconds(value);
            }
        }

        /// <summary>
        /// 唯一ID
        /// </summary>

        public object Id { get; set; }

        /// <summary>
        /// 页面HTML代码
        /// </summary>

        public string Html
        {
            get;
            set;
        }

        /// <summary>
        /// 提交的参数
        /// </summary>

        public string PostDataString
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                foreach (var kvp in PostData)
                {
                    sb.Append((sb.Length == 0 ? "" : "&") + (kvp.Key + "=" + kvp.Value));
                }
                return sb.ToString();
            }
            set
            {
                PostData.Clear();
                string[] pairs = value.Split('&');
                string key = String.Empty;
                string val = String.Empty;
                foreach (string item in pairs)
                {
                    int pos = item.IndexOf("=");

                    if (pos > 0)
                    {
                        key = item.Substring(0, pos);
                        val = item.Substring(pos + 1);
                    }
                    else
                    {
                        key = item;
                    }
                    PostData.Add(new KeyValuePair<string, string>(key, val));
                }
            }
        }

        /// <summary>
        /// 提交的参数
        /// </summary>

        public List<KeyValuePair<string, string>> PostData
        {
            get
            {
                return _PostData.Value;
            }
        }

        /// <summary>
        /// 获取一个异步
        /// </summary>
        /// <param name="success">成功后的回调函数</param>
        /// <param name="fail">失败后的回调函数</param>
        /// <returns>信号量</returns>
        public ManualResetEvent GetAsync(Action<ICsqWebResponse> success, Action<ICsqWebResponse> fail)
        {
            IHttpWebRequest request = GetWebRequest();

            if (request.Proxy == null)
            {
                string message = string.Format("代理响应失败：{0}:{1},本次抓取取消", Options.Address, Options.Port);
                Logger.Write(message);
                var done = new ManualResetEvent(false);
                done.Set();
                return done;
            }

            if (Async)
            {
                return GetAsync(request, success, fail);
            }
            else
            {
                return GetSynchronous(request, success, fail);
            }
        }

        /// <summary>
        /// 获取一个同步
        /// </summary>
        /// <param name="request">请求接口</param>
        /// <param name="success">成功后的回调函数</param>
        /// <param name="fail">失败后的回调函数</param>
        /// <returns>信号量</returns>
        public ManualResetEvent GetSynchronous(IHttpWebRequest request, Action<ICsqWebResponse> success, Action<ICsqWebResponse> fail)
        {
            var requestInfo = new SynchronousWebRequest(request);
            // do not apply options when using this method.

            requestInfo.Id = Id;
            requestInfo.CallbackSuccess = success;
            requestInfo.CallbackFail = fail;

            return requestInfo.GetResponse();
        }

        /// <summary>
        /// 获取一个异步
        /// </summary>
        /// <param name="request">请求接口</param>
        /// <param name="success">成功后的回调函数</param>
        /// <param name="fail">失败后的回调函数</param>
        /// <returns>信号量</returns>
        public ManualResetEvent GetAsync(IHttpWebRequest request, Action<ICsqWebResponse> success, Action<ICsqWebResponse> fail)
        {
            var requestInfo = new AsyncWebRequest(request);
            requestInfo.Options = Options;
            requestInfo.Id = Id;
            requestInfo.CallbackSuccess = success;
            requestInfo.CallbackFail = fail;

            return requestInfo.GetAsync();
        }

        /// <summary>
        /// 获取HTML
        /// </summary>
        /// <returns></returns>

        public string Get()
        {
            IHttpWebRequest request = GetWebRequest();
            ApplyOptions(request);

            Html = null;

            using (StreamReader responseReader = GetResponseStreamReader(request))
            {
                Html = responseReader.ReadToEnd();
            }
            return Html;
        }


        /// <summary>
        /// 从IHttpWebRequest创建对象，并返回HTML
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public string Get(IHttpWebRequest request)
        {
            Html = null;

            using (StreamReader responseReader = GetResponseStreamReader(request))
            {
                Html = responseReader.ReadToEnd();
            }
            return Html;
        }

        /// <summary>
        /// 创建IHttpWebRequest对象
        /// </summary>
        /// <returns></returns>
        public IHttpWebRequest GetWebRequest()
        {
            IHttpWebRequest request = WebRequestFactory.Create(new Uri(Url));
            ApplyOptions(request);

            request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
            return request;
        }

        /// <summary>
        /// 发起POST请求
        /// </summary>
        /// <returns></returns>
        public string Post()
        {
            return Post(Url, PostData);
        }

        /// <summary>
        /// 发起POST请求
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public string Post(string url)
        {
            return Post(url, PostData);
        }


        /// <summary>
        /// Post请求
        /// </summary>
        /// <param name="url">请求的链接</param>
        /// <param name="postData">提交的参数</param>
        /// <returns>返回值</returns>
        public string Post(string url, IEnumerable<KeyValuePair<string, string>> postData)
        {
            byte[] data = Encoding.ASCII.GetBytes(PostDataString);

            IHttpWebRequest request = WebRequestFactory.Create(new Uri(Url));
            ApplyOptions(request);

            request.Method = HttpWebRequestMethod.POST;
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = data.Length;

            var newStream = request.GetRequestStream();
            newStream.Write(data, 0, data.Length);
            newStream.Close();

            using (var response = GetResponseStreamReader(request))
            {
                Html = response.ReadToEnd();
            }
            return Html;
        }

        #region 私有方法

        /// <summary>
        /// 应用配置项
        /// </summary>
        /// <param name="request"></param>
        private void ApplyOptions(IHttpWebRequest request)
        {
            request.Timeout = (int)Math.Floor(Options.Timeout.TotalMilliseconds);
            request.UserAgent = Options.UserAgent;
            request.KeepAlive = Options.KeepAlive;
            request.Method = Options.Mehtod;

            if (Options.UseProxy)
            {
                request.Proxy = GetProxy(Options);
            }
        }

        /// <summary>
        /// 获取并验证一个代理
        /// </summary>
        /// <param name="config"></param>
        /// <returns></returns>
        private IWebProxy GetProxy(ServerConfig config)
        {
            string address = string.Format("{0}:{1}", config.Address, config.Port);
            WebProxy proxy = new WebProxy(address, true);
            proxy.UseDefaultCredentials = true;
            IHttpWebRequest request = WebRequestFactory.Create(new Uri("http://developer.baidu.com/static/developer3/html/sorry-for-ie.html" + DateTime.Now.ToString("yyyyMMddHHmmssff")));
            request.Timeout = 5000;
            request.Proxy = proxy;
            try
            {
                IHttpWebResponse response = request.GetResponse();
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    Logger.Write("代理响应正常:" + address);
                    return proxy;
                }
                return null;
            }
            catch (Exception e)
            {
                Logger.Write(string.Format("代理响应异常:{0},{1}", address, e.Message));
                return null;
            }
        }

        /// <summary>
        /// 获取响应流
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        protected StreamReader GetResponseStreamReader(IHttpWebRequest request)
        {
            var response = request.GetResponse();

            StreamReader reader;
            var encoding = GetEncoding(response);
            var stream = response.ContentEncoding == "gzip" ?
                        new GZipStream(response.GetResponseStream(), CompressionMode.Decompress) :
                        response.GetResponseStream();
            if (encoding != null)
            {
                reader = new StreamReader(stream, encoding);
            }
            else
            {
                reader = new StreamReader(stream, Encoding.UTF8, true);
            }
            return reader;
        }

        #endregion

        /// <summary>
        /// 根据页面标识，获取该页面的编码格式
        /// </summary>
        /// <param name="response"></param>
        /// <returns></returns>
        public static Encoding GetEncoding(IHttpWebResponse response)
        {
            Encoding encoding = null;

            if (!String.IsNullOrEmpty(response.ContentType)
                && response.ContentType.Contains("charset=")
                && !String.IsNullOrEmpty(response.CharacterSet))
            {
                HtmlEncoding.TryGetEncoding(response.CharacterSet, out encoding);
            }

            return encoding;
        }

        public Encoding Encoding
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }
    }
}